import Map from 'ol/Map';
import View from 'ol/View';
import {getWidth, getTopLeft} from 'ol/extent';
import TileLayer from 'ol/layer/Tile';
import WMTS from 'ol/source/WMTS';
import WMTSTileGrid from 'ol/tilegrid/WMTS';
import Projection from "ol/proj/Projection";
import WMTSCapabilities from 'ol/format/WMTSCapabilities';
import { optionsFromCapabilities } from 'ol/source/WMTS';
import { addProjection } from 'ol/proj';
import { createStringXY } from 'ol/coordinate';
import MousePosition from 'ol/control/MousePosition';
import {defaults as defaultControls} from 'ol/control';
import {HmacSHA256} from 'crypto-js';
import CryptoJS from 'crypto-js';

// 开始定义WMTS服务的重要参数 /////////////////////////////////////////
// 1 定义CGCS2000大地坐标系，代号4490
var projection = new Projection({
    code: 'EPSG:4490',
    extent: [-180, -90, 180, 90],
    units : 'm' // 这个是经纬度默认
});
/**
 * 计算resolution的方法应该是：scale * (0.0254/96) 即 缩放倍数*每像素的多少米，一般情况是96个像素占0.0254米，刚好1英寸
 * 但是ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet()方法中计算分辨率resolution时代码写成了
 * WMTS配置信息中每一层的 ScaleDenominator * 0.28E-3 / metersPerUnit
 * 这个0.28E-3的值应该是1英寸90个像素点的计算结果，即每像素(0.0254/90 = 0.00028)
 * 看来metersPerUnit就是控制像素差异的
 * 经纬度坐标系中，需要进行经纬度和米的单位换算，所以我们修改默认的这个方法
 * EPSG:4490的地图半径是6378137 与默认的不同
 * 最终返回 meter_unit 值：117673.7103642079
 * */ 
projection.getMetersPerUnit = function () {
    let meters_unit = 2 * Math.PI * 6378137 / 360;
    return meters_unit * 1.057080925592217; // 因为这个倍数是 (0.28E-3)/(0.025428516728689246/96) = 1.057080925592217
};
addProjection(projection);

const projectionExtent = projection.getExtent();
const size = getWidth(projectionExtent) / 256;  // 即 360/256 = 1.40625

/**
 * 2 计算每个层级所对应的分辨率，按照天地图统一的规则从1到20，总共20级，第0级就是一张瓦片全世界
 * 经纬度坐标系中 比例尺和分辨率的换算公式  res = scale * (0.00028) / meter_unit
 * 相当于 res = scale * (0.025428516728689246/96) / (2 * Math.PI * 6378137 / 360) 
 *           = scale * (0.025428516728689246/(2 * Math.PI * 6378137 / 360)) / 96  
 * 即 比例尺 * 1英寸的96个像素点上每个像素点是多少数值（度或米）
 * */
var resolutions = new Array(20);
for (let z = 1; z < 21; ++z) {
    // 统一的规则，从1到20
    resolutions[z] = size / Math.pow(2, z);
}


// 3 瓦片起始位置和通用的瓦片层级集合
const orgin = [-180, 90]; //经纬度坐标的瓦片起始坐标
// 这是瓦片level的通用层级数，当 matrixSet=default或缺省时，可使用这个，即地图的有效缩放级别11到20级
const default_matrixIds = [11,12,13,14,15,16,17,18,19,20]  

// 4 定义全图范围，同时也限定了地图的拖动范围
const maxExtent = [118.33968849655, 29.1885894750343, 120.725489687, 30.5651894750343];
const mousePositionControl = new MousePosition({
    coordinateFormat: createStringXY(4),
    projection: projection,
    className: 'custom-mouse-position',
    target: document.getElementById('mouse-position'),
});


// 5. 地图初始完成
var map = new Map({
    layers: [],
    target: 'map',
    controls: defaultControls().extend([mousePositionControl]),
    view: new View({
        center: [120.0187, 30.3891],
        projection: projection,
        minZoom: 7,
        maxZoom: 20,
        // extent : maxExtent, //限定到地图视图拖动范围
        zoom: 12 //地图初始化的缩放级别
    })
});



// 6. 准备加载IRS上申请的余杭地图 
const proxyUrl = "https://10.32.185.77/yh/";  // 代理工具的访问地址
// 以下示例如何使用浙江省一体化数据平台 IRS 上申请的余杭地图服务，须启动地图代理工具 https://gitee.com/w-fy/yhmap_irs.git
// 两种方式创建地图服务，第一种从Capabilities方式，第二种普通方式需指定resolutions、matrixSet 和 matrixIds
/**
 * 第一种方式：根据图层服务信息，生成ol3的图层对象
 * @param layerInfo
 * @param fn 将生成的Layer对象作为参数代入，做相应添加等操作
 * @return {ol.layer.Tile}
 */
function createLayerFromWMTSGetCapa(layerInfo, fn){
    fetch(layerInfo.capaUrl+"REQUEST=GetCapabilities").then(function(response) {
        return response.text();
    }).then(function(text) {
        let wmtsCapaParser = new WMTSCapabilities();
        let result = wmtsCapaParser.read(text);

        let options = optionsFromCapabilities(result,
                {
                    layer: layerInfo.layer, 
                    format: 'image/png'
                });
        let ress = options.tileGrid.getResolutions();
        options.urls[0] = layerInfo.capaUrl; // 用代理地址替换原地址
        let ly = new TileLayer({
            maxResolution : ress[0] * 2, // 当分辨率大于等于这个值就不显示
            //minResolution : ress[ress.length-1], // 小于这个值不显示
            source: new WMTS(options)
        });
        fn(ly);
        return ly;
    });
}

/**
 * 第二种方式，指定resolutions、matrixSet 和 matrixIds
 * 从第11级的分辨率(360/256)/Math.pow(2,11)开始到20级
 * 第11级的分层标识名EPSG:4490_yhimgmap:0，0依次到9
 */
const opt = {
    url: proxyUrl,  // 代理工具的部署访问地址，注意后台JAVA代理工具是否正确设置了token和正确的IRS地址
    layer: "yhimgmap",
    matrixSet: "EPSG:4490_yhimgmap",
    resolutions: [6.866455206208891E-4, 3.4332276031044456E-4, 1.7166138015522228E-4, 8.583069007761114E-5, 4.291534503880557E-5, 2.1457672519402785E-5, 1.0728836259701392E-5, 5.364418129850696E-6, 2.682209064925349E-6, 1.3411045324626745E-6],
    matrixIds: ['EPSG:4490_yhimgmap:0', 'EPSG:4490_yhimgmap:1', 'EPSG:4490_yhimgmap:2', 'EPSG:4490_yhimgmap:3', 'EPSG:4490_yhimgmap:4', 'EPSG:4490_yhimgmap:5', 'EPSG:4490_yhimgmap:6', 'EPSG:4490_yhimgmap:7', 'EPSG:4490_yhimgmap:8', 'EPSG:4490_yhimgmap:9']
}
const ly_img = new TileLayer({
    source: new WMTS({
        url: opt.url,
        // layer: opt.layer,  // 可缺省
        // format: 'image/png', // 可缺省
        // matrixSet: 'default',  // 可缺省或=default
        projection: projection,
        tileGrid: new WMTSTileGrid({
            origin: orgin,
            resolutions: opt.resolutions,
            // 本应该 matrixIds: opt.matrixIds 因 matrixSet=default 或缺省了,所以可使用通用的level层级数
            matrixIds: default_matrixIds 
        })
    })
})
map.addLayer(ly_img)
/*
// 后台代理提供了两个方式 
// 1）. 使用代理默认的调用参数，后台为本服务预设了相关参数，适用一个服务一个代理
createLayerFromWMTSGetCapa({
    capaUrl: proxyUrl,  // 代理工具的部署访问地址
    layer: 'yhimgmap'
}, function (ly) {
    ly.zIndex = 11;
    ly.setOpacity(0.4);
    map.addLayer(ly);
})
*/

 // 2）. 使用代理时，指定调用参数，token和IRS的地图服务地址
const proxyUrl2 = proxyUrl + "proxy"; // 代理工具的第二种访问地址
// 与地图代理端使用相同的加密策略，如果这里改动了，java代理端也需要修改
const SECRET_KEY = CryptoJS.enc.Utf8.parse("1234123412341234");
const SECRET_IV = CryptoJS.enc.Utf8.parse("1234123412341234");
/**
 * AES 对称加密json对象
 * @param {json} obj 
 * @returns 
 */
function token (obj) {
    // 因包含url，进行Base64转换后再加密
    let param = btoa(encodeURIComponent(JSON.stringify(obj)));
    let encrypted = CryptoJS.AES.encrypt(param, SECRET_KEY, {
        iv : SECRET_IV,
        mode : CryptoJS.mode.CBC,
        padding : CryptoJS.pad.Pkcs7
    });
    // 加密后的结果再进行Base64转换
    let result = encodeURIComponent(encrypted.ciphertext.toString(CryptoJS.enc.Base64));
    return result
}
const ly_img_zj = createLayerFromWMTSGetCapa({
        // capaUrl:"http://dt.yuhang.gov.cn/wmtsproxy/wmts",  // 直连方式
        capaUrl: proxyUrl2 + "?token=" + token({
            ////////// 这里替换成IRS里申请到的实际内容   //////
            url: "https://interface.yuhang.gov.cn/yhsgj/yhsgj_prd/yhsgj/yhqszyhdlxxggfwxt/yhyxdtzjtzbx",
            token: "***"  // 有了token就不会用到 appKey secretKey 了
            // appKey: "***",  // 有了token 这个可以屏蔽
            // secretKey: "***"  // 有了token 这个可以屏蔽
            ////////////////////////////////////////////////
        }) + "&",
        layer: "yhimgmapanno"  // 修改实际的图层名称，即IRS页面上地图服务参数中的 LAYER 参数所指的图层名称
    }, function(ly) {
        ly.zIndex = 11;
        // ly.setOpacity(0.4);
        map.addLayer(ly);
    }
);


/*
// 7. rest 风格加载余杭地图,IRS地图api暂不支持rest风格
fetch("http://10.32.185.77:8083/wmtsproxy/wmts/yhimgmapanno?"+"REQUEST=GetCapabilities").then(function(response) {
    return response.text();
}).then(function(text) {
    let wmtsCapaParser = new WMTSCapabilities();
    let result = wmtsCapaParser.read(text);
    let options = optionsFromCapabilities(result,
            {
                layer: "yhimgmapanno", 
                requestEncoding: 'REST'
            });
    let ress = options.tileGrid.getResolutions();
    let ly = new TileLayer({
        maxResolution : ress[0] * 2, // 当分辨率大于等于这个值就不显示
        //minResolution : ress[ress.length-1], // 小于这个值不显示
        source: new WMTS(options)
    });
    //map.addLayer(ly);
});
*/

/*
// 政务外网环境中，可添加余杭区的地图服务
// 余杭行政区划图 http://dt.yuhang.gov.cn/geocloud/gwc/demo/yhmap:bou_py?gridSet=EPSG:4490&format=image/png 相关参数查看网页源代码
const yhemap = new TileLayer({
    opacity: 1,
    zIndex: 13, // 图层处于哪一层，数字越大越在上
    minZoom: 10, // 从10级往下不显示
    maxZoom: 20, // 最大显示到20级
    // extent: [119.67716163035634, 30.161916233240206, 120.3408760860001, 30.56512001100007], // 限定范围，超出不显示
    source: new WMTS({
        url: 'http://dt.yuhang.gov.cn/geocloud/gwc/service/wmts',
        layer: 'yhmap:bou_py',
        matrixSet: 'EPSG:4490',
        format: 'image/png', //一般默认都是png
        projection: projection,
        tileGrid: new WMTSTileGrid({
            origin: orgin,
            resolutions: resolutions,
            matrixIds: ['EPSG:4490:1','EPSG:4490:2','EPSG:4490:3','EPSG:4490:4','EPSG:4490:5',
            'EPSG:4490:6','EPSG:4490:7','EPSG:4490:8','EPSG:4490:9','EPSG:4490:10',
            'EPSG:4490:11','EPSG:4490:12','EPSG:4490:13','EPSG:4490:14','EPSG:4490:15',
            'EPSG:4490:16','EPSG:4490:17','EPSG:4490:18','EPSG:4490:19','EPSG:4490:20']
        })
    })
});
// map.addLayer(yhemap);
*/

/**
// nginx发布的离线瓦片
const yhemap2 = new TileLayer({
    opacity: 1,
    zIndex: 14, // 图层处于哪一层，数字越大越在上
    minZoom: 10, // 从10级往下不显示
    maxZoom: 20, // 最大显示到20级
    // extent: [119.67716163035634, 30.161916233240206, 120.3408760860001, 30.56512001100007], // 限定范围，超出不显示
    source: new WMTS({
        url: 'http://localhost/{layer}/{TileMatrix}/{TileRow}/{TileCol}.png', // 四个参数对应了瓦片的目录结构
        requestEncoding: 'REST',
        layer: 'yhemap',  // 瓦片的顶层目录名
        matrixSet: 'ddd', // 可以不用对应到url上
        format: 'image/png', //一般默认都是png
        projection: projection,
        tileGrid: new WMTSTileGrid({
            origin: orgin,
            // 下面这个参数下载工具运行的时候打印出来了
            resolutions: [6.866455206208891E-4, 3.4332276031044456E-4, 1.7166138015522228E-4, 8.583069007761114E-5, 4.291534503880557E-5, 2.1457672519402785E-5, 1.0728836259701392E-5, 5.364418129850696E-6, 2.682209064925349E-6, 1.3411045324626745E-6],
            // 这个对应了瓦片的第二层目录名
            matrixIds: ['11','12','13','14','15','16','17','18','19','20']
        })
    })
});
map.addLayer(yhemap2);
*/

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* IRS上的强制组件 浙江省电子地图
// 以下参数来源 https://csss.zj.gov.cn/comDetail?comId=856332517729009664
const layerUrl = 'https://ibcdsg.zj.gov.cn:8443/restapi/prod/IC33000020220315000001/services/wmts/emap/default/oss';
const secretKey = '***'; // 替换自己申请的sk
const accessKey = '***'; // 替换自己申请的ak
function base64UrlEncode(str) {
    var encodedSource = CryptoJS.enc.Base64.stringify(str);
    var reg = new RegExp('/', 'g');
    encodedSource = encodedSource.replace(/=+$/, '').replace(/\+/g, '-').replace(reg, '_');
    return encodedSource;
}

let header = JSON.stringify({
    "alg": "HS256",
    "typ": "JWT"
})
let payload = JSON.stringify({
    "key": accessKey,
    "exp": new Date().setHours(new Date().getHours()+1)
});

let before_sign = base64UrlEncode(CryptoJS.enc.Utf8.parse(header)) + '.' + base64UrlEncode(CryptoJS.enc.Utf8.parse(payload));
let signature = HmacSHA256(before_sign, secretKey);
signature = base64UrlEncode(signature);
let final_sign = before_sign + '.' + signature;
const baseUrl = layerUrl + '?jwt=' + final_sign + '&x-bg-auth-type=jwt_auth' // 网关地址
const layerName = 'emap';
// 省级节点提供的瓦片总共14级，从7到20
var resolutions2 = new Array(14);
var matrixIds2 = new Array(14);
for (let z = 0; z < 14; ++z) {
    resolutions2[z] = size / Math.pow(2, z + 7);
    // 这个ids根据实际情况可能不同，如geoserver发布的就不是纯数字表示的（EPSG:4490:20）
    matrixIds2[z] = z + 7;
}
const layer_zj = new TileLayer({
    source: new WMTS({
        url: baseUrl,
        layer: layerName,
        matrixSet: 'esritilematirx',
        format: 'image/png', //一般默认都是png
        projection:  projection,
        tileGrid: new WMTSTileGrid({
            origin: orgin,
            resolutions: resolutions2,
            matrixIds: matrixIds2
        }),
        style: 'default'
    })
});
map.addLayer(layer_zj);
*/
//////////////////////////// 以上为加载省级节点的地图服务所需的鉴权及加载方式 ///////////////////////////////////////////////////////////////